{
 "cells": [
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "<div class=\"contentcontainer med left\" style=\"margin-left: -50px;\">\n",
    "<dl class=\"dl-horizontal\">\n",
    "  <dt>Title</dt> <dd> VectorField Element</dd>\n",
    "  <dt>Dependencies</dt> <dd>Matplotlib</dd>\n",
    "  <dt>Backends</dt> <dd><a href='./VectorField.ipynb'>Matplotlib</a></dd> <dd><a href='../bokeh/VectorField.ipynb'>Bokeh</a></dd>\n",
    "</dl>\n",
    "</div>"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "import numpy as np\n",
    "import holoviews as hv\n",
    "from holoviews import dim, opts\n",
    "\n",
    "hv.extension('matplotlib')"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "A ``VectorField`` plot displays velocity vectors as arrows at ``x`` and ``y`` positions with angle and magnitude components (or alternatively ``U``, ``V`` components). The element accepts the usual columnar format passing the ``(x, y, angles, magnitudes)`` components as a tuple:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "x,y  = np.mgrid[-10:10,-10:10] * 0.25\n",
    "sine_rings  = np.sin(x**2+y**2)*np.pi+np.pi\n",
    "exp_falloff = 1/np.exp((x**2+y**2)/8)\n",
    "\n",
    "vector_data = (x, y, sine_rings, exp_falloff)\n",
    "hv.VectorField(vector_data).opts(magnitude='Magnitude')"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "As you can see above, here the *x* and *y* positions are chosen to make a regular grid and the angles supplied to a ``VectorField`` are expressed in radians. The arrow angles follow a sinsoidal ring pattern, and the arrow lengths fall off exponentially from the center, so this plot has four dimensions of data (direction and length for each *x,y* position).\n",
    "\n",
    "Using the ``.opts`` method, we can also use color as a redundant indicator to the direction or magnitude:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "anglecolor = hv.VectorField(vector_data).opts(\n",
    "    opts.VectorField(title='A', magnitude='Magnitude', color='Angle'))\n",
    "magcolor = hv.VectorField(vector_data).opts(\n",
    "    opts.VectorField(title='M', magnitude='Magnitude', color='Magnitude'))\n",
    "anglecolor + magcolor"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "By default the magnitudes are rescaled to the minimum distance between individual arrows, to disable this rescaling set ``rescale_lengths=False`` use a dimension value transform to scale the size of the arrows, e.g. below we normalize the `'Magnitude'` dimension values and then scale them by a factor of `0.2`. This allows fixed scaling even when plotting arrows in an animation. Here we will vary the arrow angle with a Phase dimension and also add this angle to the magnitude data, showing the arrow angles and magnitudes varying. Due to the fixed scaling we can make out the differences across frames:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "hmap = hv.HoloMap({phase: hv.VectorField((x, y,(vector_data[2]+phase)%np.pi*2, vector_data[3]+np.abs(phase)))\n",
    "                   for phase in np.linspace(-np.pi,np.pi,5)}, kdims='Phase')\n",
    "\n",
    "hmap.opts(opts.VectorField(color='Angle', magnitude=dim('Magnitude').norm()*0.2, rescale_lengths=False))"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Vectors are also often expressed through U and V components we can easily transform these to a magnitude and angle:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "xs, ys = np.arange(0, 2 * np.pi, .2), np.arange(0, 2 * np.pi, .2)\n",
    "X, Y = np.meshgrid(xs, ys)\n",
    "U = np.cos(X)\n",
    "V = np.sin(Y)\n",
    "\n",
    "# Convert U, V to magnitude and angle\n",
    "mag = np.sqrt(U**2 + V**2)\n",
    "angle = (np.pi/2.) - np.arctan2(U/mag, V/mag)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "``VectorField`` also allows defining the ``pivot`` point of the vectors. We can for instance define ``pivot='tip'`` to pivot around the tip of the arrow. To make this clearer we will mark the pivot points:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "vectors = hv.VectorField((xs, ys, angle, mag))\n",
    "points = hv.Points((X.flat, Y.flat))\n",
    "\n",
    "(vectors * points).opts(\n",
    "    opts.Points(color='black', s=1),\n",
    "    opts.VectorField(aspect=2, color='Magnitude', cmap='fire', fig_size=300, magnitude='Magnitude', pivot='tip', scale=0.8))"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "For full documentation and the available style and plot options, use ``hv.help(hv.VectorField).``"
   ]
  }
 ],
 "metadata": {
  "language_info": {
   "name": "python",
   "pygments_lexer": "ipython3"
  }
 },
 "nbformat": 4,
 "nbformat_minor": 2
}
